#include "examplewindow.h"
#include <fstream>
#include <gtkmm/stock.h>
#include <iostream>
#include <sstream>
#include "skyline.h"


ExampleWindow::ExampleWindow()
 :   m_adjustment(1.0, 0.1, 11.0, 0.1, 1.0, 1.0), m_HScale(m_adjustment), m_Label_zoom("Zoom Level: ")
{
  space = new Rectangle(0);
  set_title("Block Placer");
  set_default_size(800, 600);

  add(m_Box); //We can put a MenuBar at the top of the box and other stuff below it.

  //Create actions for menus and toolbars:
  m_refActionGroup = Gtk::ActionGroup::create();

  //File|New sub menu:

  //File menu:
  m_refActionGroup->add( Gtk::Action::create("FileMenu", "_File") );
	m_refActionGroup->add( Gtk::Action::create("FileOpen", Gtk::Stock::OPEN),
		sigc::mem_fun(*this, &ExampleWindow::on_menu_file_open) );
	m_refActionGroup->add( Gtk::Action::create("FileSave", Gtk::Stock::SAVE),
		sigc::mem_fun(*this, &ExampleWindow::on_menu_file_save) );
  m_refActionGroup->add( Gtk::Action::create("FileQuit", Gtk::Stock::QUIT),
    sigc::mem_fun(*this, &ExampleWindow::on_menu_file_quit) );

  //Edit menu:
  m_refActionGroup->add( Gtk::Action::create("ProcessMenu", "_Process") );
  register_stock_items(); //Makes the "example_stock_rain" stock item available.
  m_refActionGroup->add( Gtk::Action::create("ProcessVerify", Gtk::StockID("process_verify") ),
    sigc::mem_fun(*this, &ExampleWindow::verify) );
  m_refActionGroup->add( Gtk::Action::create("ProcessGA", Gtk::StockID("process_GA") ),
    sigc::mem_fun(*this, &ExampleWindow::ga) );

  m_refActionGroup->add( Gtk::Action::create("ViewMenu", "_View") );
  m_refActionGroup->add( Gtk::Action::create("ViewAutofit", Gtk::Stock::ZOOM_FIT),
    sigc::mem_fun(*this, &ExampleWindow::autofit) );

  m_refUIManager = Gtk::UIManager::create();
  m_refUIManager->insert_action_group(m_refActionGroup);

  add_accel_group(m_refUIManager->get_accel_group());

  //Layout the actions in a menubar and toolbar:

  Glib::ustring ui_info =
        "<ui>"
        "  <menubar name='MenuBar'>"
        "    <menu action='FileMenu'>"
        "	    <menuitem action='FileOpen'/>"
        "	    <menuitem action='FileSave'/>"
        "	    <menuitem action='FileQuit'/>"
        "    </menu>"
        "    <menu action='ProcessMenu'>"
        "      <menuitem action='ProcessVerify'/>"
        "      <menuitem action='ProcessGA'/>"
        "    </menu>"
        "	 <menu action='ViewMenu'>"
		  "	 	<menuitem action='ViewAutofit'/>"
		  "    </menu>"
        "  </menubar>"
        "  <toolbar  name='ToolBar'>"
		  "	 <toolitem action='FileOpen'/>"
		  "	 <toolitem action='FileSave'/>"
        "    <toolitem action='FileQuit'/>"
        "    <separator />"
		  "    <toolitem action='ProcessVerify'/>"
        "    <toolitem action='ProcessGA'/>"
		  "    <toolitem action='ViewAutofit' />"
        "  </toolbar>"
        "</ui>";

  #ifdef GLIBMM_EXCEPTIONS_ENABLED
  try
  {
    m_refUIManager->add_ui_from_string(ui_info);
  }
  catch(const Glib::Error& ex)
  {
    std::cerr << "building menus failed: " <<  ex.what();
  }
  #else
  std::auto_ptr<Glib::Error> ex;
  m_refUIManager->add_ui_from_string(ui_info, ex);
  if(ex.get())
  {
    std::cerr << "building menus failed: " <<  ex->what();
  }
  #endif //GLIBMM_EXCEPTIONS_ENABLED


  //Get the menubar and toolbar widgets, and add them to a container widget:
  Gtk::Widget* pMenubar = m_refUIManager->get_widget("/MenuBar");
  if(pMenubar)
    m_Box.pack_start(*pMenubar, Gtk::PACK_SHRINK);

  m_Box.pack_start(m_HBox2, Gtk::PACK_SHRINK);



  Gtk::Widget* pToolbar = m_refUIManager->get_widget("/ToolBar") ;
  if(pToolbar)
    m_HBox2.pack_start(*pToolbar);


  m_HBox2.pack_start(m_Label_zoom, Gtk::PACK_SHRINK);
  m_HBox2.pack_start(m_HScale, Gtk::PACK_SHRINK);
  m_HScale.set_digits(1);
  m_HScale.set_size_request(200, 30);


	m_Box.pack_start(m_HBox);

  m_ScrolledWindow.add(m_TreeView);

  //Only show the scrollbars when they are necessary:
  m_ScrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);

  m_HBox.add1(frame1);
  frame1.add(m_ScrolledWindow);


  //Create the Tree model:
  m_refTreeModel = Gtk::ListStore::create(m_Columns);

  area.set_space(space);
  area.set_model(m_refTreeModel);
  area.set_adjustment(m_adjustment);
  m_TreeView.set_model(m_refTreeModel);


   //Add the TreeView's view columns:
  m_TreeView.append_column("ID", m_Columns.m_col_id); //This number will be shown with the default numeric formatting.
  m_TreeView.append_column("Width", m_Columns.m_col_w);
  m_TreeView.append_column("Height", m_Columns.m_col_h);

  m_TreeView.append_column("X", m_Columns.m_col_x);
  m_TreeView.append_column("Y", m_Columns.m_col_y);
  m_TreeView.append_column("Rotation", m_Columns.m_col_r);

  //Make all the columns reorderable:
  //This is not necessary, but it's nice to show the feature.
  //You can use TreeView::set_column_drag_function() to more
  //finely control column drag and drop.
  for(guint i = 0; i < 4 ; i++)
  {
    Gtk::TreeView::Column* pColumn = m_TreeView.get_column(i);
    pColumn->set_reorderable();
  }
  area.set_tree_view(m_TreeView.get_selection());
  m_TreeView.get_selection()->signal_changed().connect(
		sigc::mem_fun(*this, &ExampleWindow::focus)
	);
	frame2.add(area);
  	m_HBox.add2(frame2);
   area.show();
	m_HBox.set_position(250);
  show_all_children();
}

void ExampleWindow::add_stock_item(const Glib::RefPtr<Gtk::IconFactory> & factory,
                      const std::string& filepath,
                      const Glib::ustring& id, const Glib::ustring& label)
{
  Gtk::IconSource source;
  try
  {
    //This throws an exception if the file is not found:
    source.set_pixbuf( Gdk::Pixbuf::create_from_file(filepath) );
  }
  catch(const Glib::Exception& ex)
  {
    std::cout << ex.what() << std::endl;
  }

  source.set_size(Gtk::ICON_SIZE_SMALL_TOOLBAR);
  source.set_size_wildcarded(); //Icon may be scaled.

  Gtk::IconSet icon_set;
  icon_set.add_source(source); //More than one source per set is allowed.

  const Gtk::StockID stock_id(id);
  factory->add(stock_id, icon_set);
  Gtk::Stock::add(Gtk::StockItem(stock_id, label));
}

void ExampleWindow::register_stock_items()
{
  Glib::RefPtr<Gtk::IconFactory> factory = Gtk::IconFactory::create();
  add_stock_item(factory, "verify.png", "process_verify", "Verify Solution");
  add_stock_item(factory, "ga.png", "process_GA", "Solve By GA");
  factory->add_default(); //Add factory to list of factories.
}

ExampleWindow::~ExampleWindow()
{
	delete space;
}

void ExampleWindow::autofit()
{
	area.reset_zoom();
	m_adjustment.set_value(1.0);
}

void ExampleWindow::focus()
{
	Gtk::TreeModel::iterator iter = m_TreeView.get_selection()->get_selected();
	if(iter)
	{
		Gtk::TreeModel::Path path(iter);
		m_TreeView.set_cursor(path);
	}
}
void ExampleWindow::on_menu_file_quit()
{
  hide(); //Closes the main window to stop the Gtk::Main::run().
}

void ExampleWindow::on_menu_file_save()
{
  Gtk::FileChooserDialog dialog("Please choose a file", Gtk::FILE_CHOOSER_ACTION_SAVE);
  dialog.set_transient_for(*this);

  //Add response buttons the the dialog:
  dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
  dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
  dialog.set_do_overwrite_confirmation();

  //Add filters, so that only certain file types can be selected:

  Gtk::FileFilter filter_any;
  filter_any.set_name("Any file");
  filter_any.add_pattern("*");
  dialog.add_filter(filter_any);

  Gtk::FileFilter filter_oxml;
  filter_oxml.set_name("Block placer output");
  filter_oxml.add_pattern("*.bpo");
  dialog.add_filter(filter_oxml);

  dialog.set_current_name("output.bpo");


  //Show the dialog and wait for a user response:
  int result = dialog.run();

  //Handle the response:
  switch(result)
  {
    case(Gtk::RESPONSE_OK):
    {
      std::string filename = dialog.get_filename(); //Notice that it is a std::string, not a Glib::ustring.
		saveFile(filename);
      break;
    }
    case(Gtk::RESPONSE_CANCEL):
    {
      break;
    }
    default:
    {
      std::cout << "Unexpected button clicked." << std::endl;
      break;
    }
  }
}

void ExampleWindow::on_menu_file_new_generic()
{
   std::cout << "A File|New menu item was selected." << std::endl;
}

void ExampleWindow::on_menu_others()
{
  std::cout << "A menu item was selected." << std::endl;
}

void ExampleWindow::verify()
{
	typedef Gtk::TreeModel::Children type_children; //minimise code length.
	type_children children = m_refTreeModel->children();
	bool overlap = false;
	for(type_children::iterator iter = children.begin(); iter != children.end(); ++iter)
	{
		Gtk::TreeModel::Row row = *iter;
		double x1 = row[m_Columns.m_col_x] ;
		double y1 = row[m_Columns.m_col_y] ;
		double x2 = x1 + row[m_Columns.m_col_w] ;
		double y2 = y1 + row[m_Columns.m_col_h] ;
		type_children::iterator j = iter;
		j++;
		for(; j != children.end(); ++j)
		{
			Gtk::TreeModel::Row r2 = *j;
			double r2x1 = r2[m_Columns.m_col_x] ;
			double r2y1 = r2[m_Columns.m_col_y] ;
			double r2x2 = r2x1 + r2[m_Columns.m_col_w] ;
			double r2y2 = r2y1 + r2[m_Columns.m_col_h] ;

			overlap = overlap || (r2x2 > x1 && x2 > r2x1 && r2y2 > y1 && y2 > r2y1);

			if(overlap)
			{
				Gtk::MessageDialog dialog(*this, "The layout contains overlapping tiles");
				m_TreeView.get_selection()->select(j);
				dialog.run();
				break;
			}

		}
		if(overlap)
		{

			break;
		}
	}

	if(!overlap)
	{
		Gtk::MessageDialog dialog(*this, "The layout does not contain any overlapping tiles");
		dialog.run();
	}
}


void ExampleWindow::on_menu_file_open()
{
  Gtk::FileChooserDialog dialog("Please choose a file", Gtk::FILE_CHOOSER_ACTION_OPEN);
  dialog.set_transient_for(*this);

  //Add response buttons the the dialog:
  dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
  dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);

  //Add filters, so that only certain file types can be selected:

  Gtk::FileFilter filter_any;
  filter_any.set_name("Any file");
  filter_any.add_pattern("*");
  dialog.add_filter(filter_any);

  Gtk::FileFilter filter_text;
  filter_text.set_name("Input files as Text");
  filter_text.add_mime_type("text/plain");
  dialog.add_filter(filter_text);

  Gtk::FileFilter filter_xmli;
  filter_xmli.set_name("Block Placer input");
  filter_xmli.add_pattern("*.bpi");
  dialog.add_filter(filter_xmli);

  Gtk::FileFilter filter_oxml;
  filter_oxml.set_name("Block placer output");
  filter_oxml.add_pattern("*.bpo");
  dialog.add_filter(filter_oxml);


  //Show the dialog and wait for a user response:
  int result = dialog.run();

  //Handle the response:
  switch(result)
  {
    case(Gtk::RESPONSE_OK):
    {
      std::string filename = dialog.get_filename(); //Notice that it is a std::string, not a Glib::ustring.
		parseFile(filename);
      break;
    }
    case(Gtk::RESPONSE_CANCEL):
    {
      break;
    }
    default:
    {
      std::cout << "Unexpected button clicked." << std::endl;
      break;
    }
  }
}


void ExampleWindow::saveFile(std::string filename)
{
	if(filename.find(".bpo") != std::string::npos)
	{
		std::ofstream file(filename.c_str());

		file<<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"<<std::endl;
		file<<"<space x=\""<<(space->right()-space->left())<<"\" y=\""<<(space->top()-space->bottom())<<"\">"<<std::endl;

		typedef Gtk::TreeModel::Children type_children; //minimise code length.
		type_children children = m_refTreeModel->children();
		for(type_children::iterator iter = children.begin(); iter != children.end(); ++iter)
		{
			Gtk::TreeModel::Row row = *iter;

			file<<"<rect name=\""<<row[m_Columns.m_col_id]
				<<"\" x1=\""<<row[m_Columns.m_col_x]
				<<"\" y1=\""<<row[m_Columns.m_col_y]
				<<"\" x2=\""<<row[m_Columns.m_col_x] + row[m_Columns.m_col_w]
				<<"\" y2=\""<<row[m_Columns.m_col_y]+row[m_Columns.m_col_h]
				<<"\"  w=\""<<row[m_Columns.m_col_w]
				<<"\" h=\""<<row[m_Columns.m_col_h]
				<<"\" />"<<std::endl;
		}
		file<<"</space>"<<std::endl;
	}
	else 	if(filename.find(".png") != std::string::npos)
	{
		area.writeFile(filename, MyArea::PNG);
	}
	else 	if(filename.find(".pdf") != std::string::npos)
	{
		area.writeFile(filename, MyArea::PDF);
	}
	else 	if(filename.find(".ps") != std::string::npos)
	{
		area.writeFile(filename, MyArea::PS);
	}
}

void ExampleWindow::ga()
{
		std::ofstream file("gainput.txt");

		file<<(space->right()-space->left())<<" "<<(space->top()-space->bottom())<<std::endl;

		std::vector<Rectangle> placed, unplaced;


		typedef Gtk::TreeModel::Children type_children; //minimise code length.
		type_children children = m_refTreeModel->children();
		for(type_children::iterator iter = children.begin(); iter != children.end(); ++iter)
		{
			Gtk::TreeModel::Row row = *iter;

			if(row[m_Columns.m_col_y] + row[m_Columns.m_col_h] < 0.6*(space->top()-space->bottom()))
			{
				Rectangle r(row[m_Columns.m_col_id]);

				r.size(row[m_Columns.m_col_h], row[m_Columns.m_col_w]);
				r.place(row[m_Columns.m_col_x], row[m_Columns.m_col_y]);
				placed.push_back(r);
			}
			else
			{
				Rectangle r(row[m_Columns.m_col_id]);

				r.size(row[m_Columns.m_col_h], row[m_Columns.m_col_w]);
				r.place(0,0);
				unplaced.push_back(r);
			}
		}
		file<<placed.size()<<std::endl;
		for(unsigned i = 0; i < placed.size(); i++)
		{
			file<<placed[i];
		}
		file<<unplaced.size()<<std::endl;
		for(unsigned i = 0; i < unplaced.size(); i++)
		{
			file<<unplaced[i].right() - unplaced[i].left()<<" "<<unplaced[i].top() - unplaced[i].bottom()<<std::endl;
		}

}

void ExampleWindow::parseFile(std::string filename)
{
	std::ifstream file(filename.c_str());

	if(filename.find(".bpo") != std::string::npos)
	{
		try
		{
			m_refTreeModel->clear();
			Gtk::TreeModel::Row row;
			xmlpp::DomParser parser;
			parser.set_substitute_entities(); //We just want the text to be resolved/unescaped automatically.
			parser.parse_file(filename);
			if(parser)
			{
				//Walk the tree:
				const xmlpp::Node* rnode = parser.get_document()->get_root_node(); //deleted by DomParser.
				Glib::ustring nodename = rnode->get_name();
				if(nodename == "space")
				{
					const xmlpp::Element* rootNodeElement = dynamic_cast<const xmlpp::Element*>(rnode);
					std::stringstream s;
//					s<<nodeElement->get_attribute("name")->get_value()<<" ";
					s<<rootNodeElement->get_attribute("x")->get_value()<<" ";
					s<<rootNodeElement->get_attribute("y")->get_value()<<" ";

					int maxx, maxy;
					s >> maxx >> maxy;

					space->size(maxy, maxx);

					xmlpp::Node::NodeList list = rnode->get_children();

					for(xmlpp::Node::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter)
					{
						const xmlpp::Node* node = *iter;
						Glib::ustring nodename = node->get_name();

						if(nodename == "rect")
						{
							const xmlpp::Element* nodeElement = dynamic_cast<const xmlpp::Element*>(node);
							std::stringstream s;
							s<<nodeElement->get_attribute("name")->get_value()<<" ";
							s<<nodeElement->get_attribute("x1")->get_value()<<" ";
							s<<nodeElement->get_attribute("x2")->get_value()<<" ";
							s<<nodeElement->get_attribute("y1")->get_value()<<" ";
							s<<nodeElement->get_attribute("y2")->get_value()<<" ";

							int id, x1, x2, y1, y2;
							s >> id >> x1 >> x2 >> y1 >> y2;
							row = *(m_refTreeModel->append());
							row[m_Columns.m_col_id] = id;
							row[m_Columns.m_col_h] = y2-y1;
							row[m_Columns.m_col_w] = x2-x1;
							row[m_Columns.m_col_x] = x1;
							row[m_Columns.m_col_y] = y1;
							row[m_Columns.m_col_r] = 0;
							row[m_Columns.m_col_red] = 0;
							row[m_Columns.m_col_green] = 0;
							row[m_Columns.m_col_blue] = 0;
						}
					}
				}

			}
			m_adjustment.value_changed();
			area.paint();
		}
		catch(const std::exception& ex)
		{
			std::cout << "Exception caught: " << ex.what() << std::endl;
		}
	}
	else
	{
		int h,w;
		file>>w>>h;
		m_refTreeModel->clear();
		space->size(h, w);
		int n;
		file>>n;
		Gtk::TreeModel::Row row;
		for(int i = 0; i < n && file.good(); i++)
		{
			//Fill the TreeView's model
			Rectangle r(i);
			file>>r;

			row = *(m_refTreeModel->append());
			row[m_Columns.m_col_id] = r.name();
			row[m_Columns.m_col_h] = r.top()-r.bottom();
			row[m_Columns.m_col_w] = r.right()-r.left();
			row[m_Columns.m_col_x] = r.left();
			row[m_Columns.m_col_y] = r.bottom();
			row[m_Columns.m_col_r] = 0;
			row[m_Columns.m_col_red] = 0;
			row[m_Columns.m_col_green] = 0;
			row[m_Columns.m_col_blue] = 0;
		}
		if(!file && !file.eof())
		{
			std::cerr<<"Bad file format";
		}
		Skyline s(w,h,m_refTreeModel);
		int hf = s.solve();
		if(hf > h)
		{
			std::string s("The height to pack these rectangles is ");
			std::stringstream str;
			str<<s<<hf;
			std::getline(str,s);
			Gtk::MessageDialog dialog(*this, s);
			dialog.run();
		}

		m_adjustment.value_changed();
		area.paint();

	}
}
